import { inject, injectable } from 'inversify';
import { PermissionService } from '@2/app/core/services/permission.service';
import { TYPES } from '@2/app/core/ioc/types';
import { UserStorage } from '@2/app/core/storage/user.storage';
import { PermissionStorage } from '@2/app/core/storage/permission.storage';
import { AppRouteRecordRaw } from '@/router/types';
import { toRaw } from 'vue';
import projectSetting from '@/settings/projectSetting';
import { ProjectConfig } from '#/config';
import { PermissionModeEnum } from '@/enums/appEnum';
import { filter } from 'xe-utils';
import { asyncRoutes } from '@/router/routes';
import { flatMultiLevelRoutes, transformObjToRoute } from '@/router/helper/routeHelper';
import { transformRouteToMenu } from '@/router/helper/menuHelper';
import { useMessage } from '@/hooks/web/useMessage';
import { ERROR_LOG_ROUTE, PAGE_NOT_FOUND_ROUTE } from '@/router/routes/basic';
import { AppUser } from '@2/typings/model/app.user';
import { PageEnum } from '@/enums/pageEnum';
import { AppStorage } from '@2/app/core/storage/app.storage';
import { PermissionModel } from '@2/typings/model/permission.model';
import { RouteItem } from '@2/typings/router';
import { useI18n } from '@/hooks/web/useI18n';

/**
 * @program: microservice-central-portal
 * @description: 权限服务实现
 * @author: baiHoo.chen
 * @create: 2022年5月26日
 **/
@injectable()
export class PermissionServiceImpl implements PermissionService {
  constructor(
    @inject(TYPES.UserStorage) protected userStorage: UserStorage,
    @inject(TYPES.PermissionStorage) protected permissionStorage: PermissionStorage,
    @inject(TYPES.AppStorage) protected appStorage: AppStorage,
  ) {}

  public async buildRoutesAction(): Promise<AppRouteRecordRaw[]> {
    // 1. 使用定义局部国际化消息通知函数
    const { t } = useI18n();
    // 2. 使用定义局部路由集合
    let routes: AppRouteRecordRaw[] = [];
    // 3. 使用定义用户角色集合
    const roleList = toRaw(this.userStorage.getAppUser().roles) || [];
    // 4. 使用定义项目配置权限模式
    const { permissionMode = projectSetting.permissionMode } =
      this.appStorage.useDefineStore().projectConfig || ({} as ProjectConfig);
    // 5. 定义路由过滤器
    const routeFilter = (route: AppRouteRecordRaw) => {
      const { meta } = route;
      const { roles } = meta || {};
      if (!roles) return true;
      return roleList.some((role) => roles.some((rr) => rr == role.code));
    };
    // 5. 定义路由移除忽略过滤器
    const routeRemoveIgnoreFilter = (route: AppRouteRecordRaw) => {
      const { meta } = route;
      // ignoreRoute 为true 则路由仅用于菜单生成，不会在实际的路由表中出现
      const { ignoreRoute } = meta || {};
      // arr.filter 返回 true 表示该元素通过测试
      return !ignoreRoute;
    };
    switch (permissionMode) {
      // 角色权限
      case PermissionModeEnum.ROLE:
        // 对非一级路由进行过滤
        routes = filter(asyncRoutes, routeFilter);
        // 对一级路由根据角色权限过滤
        routes = routes.filter(routeFilter);
        // Convert multi-level routing to level 2 routing
        // 将多级路由转换为 2 级路由
        routes = flatMultiLevelRoutes(routes);
        break;

      // 路由映射， 默认进入该case
      case PermissionModeEnum.ROUTE_MAPPING:
        // 对非一级路由进行过滤
        routes = filter(asyncRoutes, routeFilter);
        // 对一级路由再次根据角色权限过滤
        routes = routes.filter(routeFilter);
        // 将路由转换成菜单
        const menuList = transformRouteToMenu(routes, true);
        // 移除掉 ignoreRoute: true 的路由 非一级路由
        routes = filter(routes, routeRemoveIgnoreFilter);
        // 移除掉 ignoreRoute: true 的路由 一级路由；
        routes = routes.filter(routeRemoveIgnoreFilter);
        // 对菜单进行排序
        menuList.sort((a, b) => {
          return (a.meta?.orderNo || 0) - (b.meta?.orderNo || 0);
        });

        // 设置菜单列表
        this.permissionStorage.setFrontMenuList(menuList);

        // Convert multi-level routing to level 2 routing
        // 将多级路由转换为 2 级路由
        routes = flatMultiLevelRoutes(routes);
        break;
      // 后台动态处理权限和菜单
      case PermissionModeEnum.BACK:
        const { createMessage } = useMessage();

        createMessage.loading({
          content: t('sys.app.menuLoading'),
          duration: 1,
        });
        let routeList: AppRouteRecordRaw[] = [];
        try {
          // TODO 后台获取用户转化后的路由菜单
          routeList = this.getTransformMenuList();
        } catch (error) {
          console.error(error);
        }
        // 动态引入组件
        routeList = transformObjToRoute(routeList);
        // 后台路由到菜单结构
        const backMenuList = transformRouteToMenu(routeList);
        this.permissionStorage.setBackMenuList(backMenuList);

        // remove meta.ignoreRoute item
        routeList = filter(routeList, routeRemoveIgnoreFilter);
        routeList = routeList.filter(routeRemoveIgnoreFilter);

        routeList = flatMultiLevelRoutes(routeList);
        // TODO 临时用 asyncRoutes 测试
        routes = [PAGE_NOT_FOUND_ROUTE, ...asyncRoutes];
        // routes = [PAGE_NOT_FOUND_ROUTE, ...routeList];
        break;
    }
    routes.push(ERROR_LOG_ROUTE);
    this.patchHomeAffix(routes);
    return routes;
  }
  /**
   * 转变 allPermList 成 并获取用户菜单
   * @private
   */
  private getTransformMenuList(): RouteItem[] {
    const treeList: RouteItem[] = [];
    const metaList: PermissionModel[] = this.userStorage.getAppUser().menus;
    this.transformTreeMenuList(treeList, metaList, null);
    return treeList;
  }
  /**
   * 转化菜单树结构列表
   * @param treeList
   * @param metaList
   * @param temp
   */
  private transformTreeMenuList(
    treeList: RouteItem[],
    metaList: PermissionModel[],
    temp: RouteItem | null,
  ): void {
    metaList.forEach((perm) => {
      if (perm.menuType != 2) {
        const tempPid: number = perm.parentId;
        const tree: RouteItem = {
          id: perm.id,
          path: perm.path,
          component: perm.component,
          meta: {
            hideMenu: perm.alwaysShow != 1,
            showMenu: perm.alwaysShow == 1,
            ignoreKeepAlive: perm.keepAlive == 0,
            title: perm.title,
            icon: perm.icon,
            orderNo: perm.sort,
            currentActiveMenu: perm.currentActiveMenu,
          },
          name: perm.name,
          redirect: perm.redirect,
          caseSensitive: true,
          children: [],
          leafed: perm.leafed != 0,
        };
        if (temp == null && tempPid == 0) {
          treeList.push(tree);
          // 是否包含叶子节点
          if (tree.leafed) {
            this.transformTreeMenuList(treeList, metaList, tree);
          }
        } else if (temp != null && tempPid != 0 && tempPid == temp.id) {
          temp.children.push(tree);
          // 是否包含叶子节点
          if (tree.leafed) {
            this.transformTreeMenuList(treeList, metaList, tree);
          }
        }
      }
    });
  }
  /**
   * 根据设置的首页path，修正routes中的affix标记（固定首页）
   * @param routes
   * @private
   */
  private patchHomeAffix(routes: AppRouteRecordRaw[]) {
    if (!routes || routes.length === 0) return;
    const appUser: AppUser = this.userStorage.getAppUser();
    let homePath: string = appUser.homePath || PageEnum.BASE_HOME;
    function patcher(routes: AppRouteRecordRaw[], parentPath = '') {
      if (parentPath) parentPath = parentPath + '/';
      routes.forEach((route: AppRouteRecordRaw) => {
        const { path, children, redirect } = route;
        const currentPath = path.startsWith('/') ? path : parentPath + path;
        if (currentPath === homePath) {
          if (redirect) {
            homePath = route.redirect! as string;
          } else {
            route.meta = Object.assign({}, route.meta, { affix: true });
            throw new Error('end');
          }
        }
        children && children.length > 0 && patcher(children, currentPath);
      });
    }
    try {
      patcher(routes);
    } catch (e) {
      // 已处理完毕跳出循环
    }
    return;
  }
}
